import java.awt.Color
import java.awt.Graphics2D
import java.awt.Image
import java.awt.Rectangle
import java.awt.image.BufferedImage
import org.openscience.cdk.AtomContainer
import org.openscience.cdk.DefaultChemObjectBuilder
import org.openscience.cdk.Molecule
import org.openscience.cdk.aromaticity.CDKHueckelAromaticityDetector
import org.openscience.cdk.inchi.InChIToStructure
import org.openscience.cdk.interfaces.IAtomContainer
import org.openscience.cdk.io.SDFWriter
import org.openscience.cdk.layout.StructureDiagramGenerator
import org.openscience.cdk.renderer.Renderer
import org.openscience.cdk.renderer.font.AWTFontManager
import org.openscience.cdk.renderer.generators.BasicAtomGenerator
import org.openscience.cdk.renderer.generators.IGenerator
import org.openscience.cdk.renderer.generators.RingGenerator
import org.openscience.cdk.renderer.visitor.AWTDrawVisitor
import org.openscience.cdk.tools.manipulator.AtomContainerManipulator

/**
 * this service is used to render a structure into a mol file or a graph
 */


class RenderService {

  boolean transactional = true

  /**
   * renders a mol file from a compound
   */
  String renderToMol(Compound compound) {
    assert compound != null, "sorry you need to provide a compound!"
    return renderToMol(compound.inchi)
  }

  /**
   * generates an atom container from the inchi code
   */
  def renderToAtomContainer(String inchi) {

    assert inchi != null, "sorry you need to provide an inchi!"
    try {
      InChIToStructure convert = new InChIToStructure(inchi, DefaultChemObjectBuilder.getInstance())
      IAtomContainer container = convert.getAtomContainer()
      return container
    }
    catch (Exception e) {
      log.warn "${e.getMessage()} - ${inchi}"
      return new AtomContainer()
    }
  }

  /**
   * renders an inchi code to mol
   */
  String renderToMol(String inchi) {

    IAtomContainer container = renderToAtomContainer(inchi)
    StringWriter swriter = new StringWriter()
    SDFWriter writer = new SDFWriter(swriter)
    writer.write(container)
    writer.close()
    swriter.flush()
    swriter.close()

    String result = swriter.getBuffer().toString()
    log.debug "generated sdf: ${result}"
    return result

  }
  /**
   * renders an image from a mol file
   */
  def renderToImage(Compound compound, int width = 120, int height = 120, Color color = Color.WHITE) {
    return renderToImage(compound.inchi, width, height, color)
  }

/**
 * renders an image from a mol file
 */
  Image renderToImage(String inchi, int width = 120, int height = 120, Color color = Color.WHITE) {


    BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB)


    try {

      StructureDiagramGenerator sdg = new StructureDiagramGenerator()
      def mol = renderToAtomContainer(inchi)

      AtomContainerManipulator.percieveAtomTypesAndConfigureAtoms(mol);
      CDKHueckelAromaticityDetector.detectAromaticity(mol);

      sdg.setMolecule(new Molecule(mol))
      sdg.generateCoordinates()


      mol = sdg.getMolecule()

      // generators make the image elements
      List<IGenerator> generators = new ArrayList<IGenerator>()
      generators.add(new RingGenerator())

      generators.add(new BasicAtomGenerator())

      // the renderer needs to have a toolkit-specific font manager
      Renderer renderer = new Renderer(generators, new AWTFontManager())

      renderer.getRenderer2DModel().setFitToScreen true
      renderer.getRenderer2DModel().setShowAtomTypeNames false
      renderer.getRenderer2DModel().setShowImplicitHydrogens false
      renderer.getRenderer2DModel().setShowExplicitHydrogens true
      renderer.getRenderer2DModel().setUseAntiAliasing true
      renderer.getRenderer2DModel().setBoundsColor Color.darkGray
      renderer.getRenderer2DModel().setScale 0.8

      // the call to 'setup' only needs to be done on the first paint
      Rectangle drawArea = new Rectangle(width, height)
      renderer.setup(mol, drawArea);

      // paint the background
      Graphics2D g2 = (Graphics2D) image.getGraphics();
      g2.setColor(color);
      g2.fillRect(0, 0, width, height);

      // the paint method also needs a toolkit-specific renderer
      renderer.paintMolecule(mol, new AWTDrawVisitor(g2), drawArea, true);
    }
    catch (Exception e) {
      log.warn "${e.getMessage()} - ${inchi}"
      image.getGraphics().drawString "error!", 0, 0

    }
    return image

  }

}
